package mysql

import (
	"database/sql"
	"fmt"
	stdLog "log"
	"os"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	"github.com/super-team/start-kit/configs"
	"github.com/super-team/start-kit/tools/log"
)

var (
	DB   *gorm.DB
	once sync.Once
)

func InitMysql() {
	once.Do(func() {
		cfg := configs.Config.Mysql
		dns := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
			cfg.Username, cfg.Password, cfg.Host, cfg.DB)
		d, err := sql.Open("mysql", dns)
		if err != nil {
			panic(err)
		}
		d.SetMaxIdleConns(cfg.MaxIdleConns)
		d.SetMaxOpenConns(cfg.MaxOpenConns)
		d.SetConnMaxLifetime(time.Duration(cfg.MaxLifeTime) * time.Second)
		err = d.Ping()
		if err != nil {
			panic(err)
		}
		level := logger.Error
		if configs.Config.Server.Debug {
			level = logger.Info
		}
		newLogger := logger.New(
			stdLog.New(os.Stdout, "\r\n", stdLog.LstdFlags), // io writer
			logger.Config{
				SlowThreshold: time.Second * 5,             // 慢 SQL 阈值
				LogLevel:      level,                       // Log level
				Colorful:      configs.Config.Server.Debug, // 禁用彩色打印
			},
		)
		db, err := gorm.Open(mysql.New(mysql.Config{
			DefaultStringSize:         256,   // add default size for string fields, by default, will use db type `longtext` for fields without size, not a primary key, no index defined and don't have default values
			DisableDatetimePrecision:  true,  // disable datetime precision support, which not supported before MySQL 5.6
			DontSupportRenameIndex:    true,  // drop & create index when rename index, rename index not supported before MySQL 5.7, MariaDB
			DontSupportRenameColumn:   true,  // use change when rename column, rename rename not supported before MySQL 8, MariaDB
			SkipInitializeWithVersion: false, // smart configure based on used version
			Conn:                      d,
		}), &gorm.Config{
			SkipDefaultTransaction: true,
			PrepareStmt:            true,
			Logger:                 newLogger,
		})
		if err != nil {
			d.Close()
			panic(err)
		}
		DB = db
	})
}

func Close() {
	if DB != nil {
		d, err := DB.DB()
		if err != nil {
			log.Log.Err(err).Send()
		}
		if err := d.Close(); err != nil {
			log.Log.Err(err).Send()
		}
	}
}
